package com.inetools.gwetons.shared.logging;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.gwt.core.client.GWT;
import com.google.gwt.logging.client.LogConfiguration;

public class LogUtilImpl implements LogUtil {
		Logger logger = null;
			Object loggerLock = new Object();
			@Override
			public Logger getLogger() {
				synchronized( loggerLock) {
					return logger;
				} // synchronized
			}
			@Override
			public String getLoggerName() {
				synchronized( loggerLock) {
					if ( logger == null) return null;
					return logger.getName();
				}
			}
			@Override
			public void setLogger( Logger logger) {
				synchronized( loggerLock) {
					this.logger = logger;
				}
			}
			@Override
			public Logger setUpLogger( String loggerName) {
				if ( loggerName == null) {
					return null;
				}
				synchronized( loggerLock) {
					Logger loggerObj = getLogger();
						if ( ( loggerObj != null) && ( loggerName.equals( loggerObj.getName()))) {
							return loggerObj;
						}
					loggerObj = Logger.getLogger( loggerName);
					setLogger( loggerObj);
					return loggerObj;
				}
			}
			
		// log methods ----------------------------------------------------------------------------
		@Override
		public void log( Level level, String message) {
			if ( GWT.isClient()) {
				if ( !LogConfiguration.loggingIsEnabled()) return;
			}
			
			Logger loggerObj = getLogger();
				if ( loggerObj == null) return;
				if ( !loggerObj.isLoggable( level)) return;
				
//Remove the following if-blocks of println statements once figure out how to make logger work with GWT (DEV mode and PROD mode)
/*						
if ( !GWT.isScript()) {
	if ( level.intValue() > Level.INFO.intValue()) {
		System.err.println( "[" + level.getName() + "] " + message);
	}
	else {
		System.out.println( "[" + level.getName() + "] " + message);
	}
}
*/
//TODO Research for manually setting Firebug console (and other console) for logging, not via GWT's logging system since GWT's logging system is not totally reliable. 
			loggerObj.log( level, message);
		}
		
		@Override
		public void log( Level level, String message, Throwable throwable) {
			if ( GWT.isClient()) {
				if ( !LogConfiguration.loggingIsEnabled()) return;
			}
			
			Logger loggerObj = getLogger();
				if ( loggerObj == null) return;
				if ( !loggerObj.isLoggable( level)) return;
			
//Remove the following if-blocks of println statements once figure out how to make logger work with GWT (DEV mode and PROD mode) 
/*
if ( !GWT.isScript()) {
	if ( level.intValue() > Level.INFO.intValue()) {
		System.err.println( "[" + level.getName() + "] " + message);
		System.err.println( throwable);
	}
	else {
		System.out.println( "[" + level.getName() + "] " + message);
		System.out.println( throwable);
	}
}
*/
			loggerObj.log( level, message, throwable);
		}
		
		@Override
		public void log( String levelStr, String message) {
			try {
				Level level = Level.parse( levelStr);
				log( level, message);
			}
			catch( Throwable throwable) {
				log( 
						Level.SEVERE, 
						"The value of levelStr input is not appropriate: " 
						+ ((levelStr == null) ? "null" : levelStr), 
						throwable
						);
			}
		}
		// ----------------------------------------------------------------------------------------
		
		// logEntering methods --------------------------------------------------------------------
		/**
		 * For logging method entering. <br />
		 * In this implementation, it is expected for the args argument to have the next elements 
		 * in the order:
		 * <ol>
		 * <li>name of method being entered (String type)</li>
		 * <li>Optional message (String type) for the arguments of the method being entered.<br />
		 * When the method being entered does not have no arguments, eliminate this, or provide any 
		 * object such as null for this element and give true for next boolean element.</li>
		 * <li>Optional boolean element to specify whether ignore the previous message element. <br />
		 * When this value is true, then the previous element will be ignored as the method being 
		 * entered has no arguments. <br /> 
		 * This Boolean element is necessary when the method being entered has no arguments and like to 
		 * specify other level than FINER by next element.</li> 
		 * <li>Optional Level element. <br />
		 * If emitted, then FINER level will be used.</li>
		 * </ol>
		 */
		@Override
		public void logEntering( Object... args) {
			if ( GWT.isClient()) {
				if ( !LogConfiguration.loggingIsEnabled()) return;
			}
			
			Logger loggerObj = getLogger();
				if ( loggerObj == null) return;
			
			// Validating args[0] -------------------------------------------------------------
			if ( args.length < 1) {
				log( 
						Level.WARNING, 
						"Could not log method's entering since none argument is provided."
						);
				return;
			}
			if ( args[ 0] == null) {
				log( 
						Level.WARNING, 
						"Could not log method's entering since missing name of method being entered."
						);
				return;
			}
			if ( !( args[ 0] instanceof String)) {
				log( 
						Level.WARNING, 
						"Could not log method's entering since first argument is not String instance " 
						+ "to provide name of method being entered."
						);
				return;
			}
			// --------------------------------------------------------------------------------
			
			boolean is2ndArgIgnored = false;
			Level level = Level.FINER;
			if ( args.length > 2) {
				if ( args[ 2] instanceof Level) {
					level = (Level)args[ 2];
				}
				else {
					if ( args[ 2] instanceof Boolean) {
						is2ndArgIgnored = ((Boolean)args[ 2]).booleanValue();
					}
					else if ( args[ 2] instanceof String) {
						try {
							is2ndArgIgnored = Boolean.valueOf( (String)args[ 2]);
						}
						catch( Exception exception) { // Do nothing
						}
					}
					
					if ( args.length > 3) {
						if ( args[ 3] instanceof Level) {
							level = (Level)args[ 3];
						}
						else if ( args[ 3] instanceof String) {
							try {
								level = Level.parse( (String)args[ 3]);
							}
							catch( Exception exception) { // Do nothing
							}
						}
					}
				}
			}
			else if ( args.length < 2) {
				is2ndArgIgnored = true;
			}
			
			if ( loggerObj.isLoggable( level)) {
				String sourceMethod = (String)args[ 0];
				 
				String message 
				= "Entered " + sourceMethod + " method";
					if ( !is2ndArgIgnored) {
						message 
						= message + ((args[ 1] == null) ? "" : " " + args[ 1].toString());
					}
				log( level, message);
			}
		}
		protected void logEntering( String sourceMethod, Object[] params, Level level) {
			ArrayList<Object> argsArrayList = new ArrayList<Object>();
				argsArrayList.add( sourceMethod);
				String additionalMessage = "of " + getLoggerName() + " class";
					if ( params == null) {
						argsArrayList.add( additionalMessage);
						argsArrayList.add( false);
					}
					else if ( params.length < 1) {
						argsArrayList.add( additionalMessage);
						argsArrayList.add( false);
					}
					else {
						String paramsStr = "";
							for( Object paramObj : params) {
								paramsStr 
								= paramsStr + ((paramObj == null) ? "null" : paramObj.toString()) + ", ";
							}
						additionalMessage 
						= additionalMessage + " with argument(s) of " 
								+ paramsStr.substring( 0, paramsStr.lastIndexOf( ","));
						argsArrayList.add( additionalMessage);
						argsArrayList.add( false);
					}
				Level levelObj = level;
					if ( levelObj == null) levelObj = Level.FINER;
					argsArrayList.add( levelObj);
			logEntering( argsArrayList.toArray());
		}
		// ----------------------------------------------------------------------------------------
		
		// entering methods -----------------------------------------------------------------------
		@Override
		public void entering( String sourceMethod, Object[] params) {
			if ( GWT.isClient()) {
				if ( !LogConfiguration.loggingIsEnabled()) return;
			}
			
			Logger loggerObj = getLogger();
				if ( loggerObj == null) return;
				
//			loggerObj.entering( getLoggerName(), sourceMethod, params);
//TODO switch to use underneath Logger.entering method instead of below code by uncommenting statement above once Logger.entering method starts working as expected
			logEntering( sourceMethod, params, Level.FINER);
		}
		// ----------------------------------------------------------------------------------------
		
		// logExiting methods ---------------------------------------------------------------------
		/**
		 * For logging method exiting. <br />
		 * In this implementation, it is expected for the args argument to have the next elements 
		 * in the order:
		 * <ol>
		 * <li>name of method being exited (String type)</li>
		 * <li>Optional object being returned by method<br />
		 * When the method being exited returns void, eliminate this, or provide any object such as 
		 * null for this element and give true for next boolean element.</li>
		 * <li>Optional boolean element to specify whether ignore the previous element of object 
		 * being returned by method. <br />
		 * When this value is true, then the previous element will be ignored as the method being 
		 * exited returns void. <br /> 
		 * This Boolean element is necessary when the method being exited returns void and like to 
		 * specify other level than FINER by next element.</li> 
		 * <li>Optional Level element. <br />
		 * If emitted, then FINER level will be used.</li>
		 * </ol>
		 */
		@Override
		public void logExiting( Object... args) {
			if ( GWT.isClient()) {
				if ( !LogConfiguration.loggingIsEnabled()) return;
			}
			
			Logger loggerObj = getLogger();
				if ( loggerObj == null) return;
				
			// Validating args[0] -------------------------------------------------------------
			if ( args.length < 1) {
				log( 
						Level.WARNING, 
						"Could not log method's exiting since none argument is provided."
						);
				return;
			}
			if ( args[ 0] == null) {
				log( 
						Level.WARNING, 
						"Could not log method's exiting since missing name of method being exited."
						);
				return;
			}
			if ( !( args[ 0] instanceof String)) {
				log( 
						Level.WARNING, 
						"Could not log method's exiting since first argument is not String instance " 
						+ "to provide name of method being exited."
						);
				return;
			}
			// --------------------------------------------------------------------------------
			
			boolean is2ndArgIgnored = false;
			Level level = Level.FINER;
			if ( args.length > 2) {
				if ( args[ 2] instanceof Level) {
					level = (Level)args[ 2];
				}
				else {
					if ( args[ 2] instanceof Boolean) {
						is2ndArgIgnored = ((Boolean)args[ 2]).booleanValue();
					}
					else if ( args[ 2] instanceof String) {
						try {
							is2ndArgIgnored = Boolean.valueOf( (String)args[ 2]);
						}
						catch( Exception exception) { // Do nothing
						}
					}
					
					if ( args.length > 3) {
						if ( args[ 3] instanceof Level) {
							level = (Level)args[ 3];
						}
						else if ( args[ 3] instanceof String) {
							try {
								level = Level.parse( (String)args[ 3]);
							}
							catch( Exception exception) { // Do nothing
							}
						}
					}
				}
			}
			else if ( args.length < 2) {
				is2ndArgIgnored = true;
			}
			
			if ( loggerObj.isLoggable( level)) {
				String sourceMethod = (String)args[ 0];
				 
				String message 
				= "Exited " + sourceMethod + " method ";
					if ( !is2ndArgIgnored) {
						message 
						= message + " with returning " 
								+ ((args[ 1] == null) ? "null" : args[ 1].toString());
					}
				log( level, message);
			}
		}
		protected void logExiting( String sourceMethod, Object[] result, Level level) {
			if ( GWT.isClient()) {
				if ( !LogConfiguration.loggingIsEnabled()) return;
			}
			
			Logger loggerObj = getLogger();
				if ( loggerObj == null) return;
			
			Level levelObj = level;
				if ( levelObj == null) levelObj = Level.FINER;
			if ( loggerObj.isLoggable( levelObj)) {
				String message = "Exited " + sourceMethod + " method of " + getLoggerName() + " class";
				if ( result != null) {
					if ( result.length > 0) {
						message = message + " with returning " 
								+ ((result[ 0] == null) ? "null" : result[ 0].toString());
					}
				}
				log( levelObj, message);
			}
		}
		// ----------------------------------------------------------------------------------------
		
		// exiting methods ------------------------------------------------------------------------
		@Override
		public void exiting( String sourceMethod) {
			if ( GWT.isClient()) {
				if ( !LogConfiguration.loggingIsEnabled()) return;
			}
			
			Logger loggerObj = getLogger();
				if ( loggerObj == null) return;
			
//			loggerObj.exiting( getLoggerName(), sourceMethod);
//TODO switch to use underneath Logger.exiting method instead of below code by uncommenting statement above once Logger.exiting method starts working as expected
			logExiting( sourceMethod, null, null);
		}
		@Override
		public void exiting( String sourceMethod, Object result) {
			if ( GWT.isClient()) {
				if ( !LogConfiguration.loggingIsEnabled()) return;
			}
			
			Logger loggerObj = getLogger();
				if ( loggerObj == null) return;
			
//			loggerObj.exiting( getLoggerName(), sourceMethod, result);
//TODO switch to use underneath Logger.exiting method instead of below code by uncommenting statement above once Logger.exiting method starts working as expected
			logExiting( sourceMethod, new Object[]{ result}, null);
		}
		// ----------------------------------------------------------------------------------------
	}